# -*- coding: utf-8 -*-
#
# Collector, the Audio CD collection manager
# Copyright (C) 2006 Piotr Gaczkowski
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

import gobject 
import dbus
import sys
import musicbrainz2.disc as mbdisc
import threading

if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
  import dbus.glib

from col.disc import disc_pack
from col.debug import debug
from col.gatherer import Gatherer
import col.config

class Collector:

  def __init__(self, localedir):

    # Initialize threading for mainloop
    gobject.threads_init()

    # Initialize Hal support through D-Bus
    self.bus = dbus.SystemBus()
    self.hal_manager = dbus.Interface(self.bus.get_object('org.freedesktop.Hal', 
                                     '/org/freedesktop/Hal/Manager'),
                                 'org.freedesktop.Hal.Manager')

    # Catch all DeviceAdded signals
    self.hal_manager.connect_to_signal('DeviceAdded', self._device_added_callback)

    # List of devices to lock
    self.locked_devs = []

    # A pack of discs - container for all the processed CDs
    self.discs = disc_pack()

    # Read configuration
    # TODO: Parse the config file into an object
    self.config = col.config.Config()
    self.conf = dict()
    if self.config.get('fallback') == '0':
      self.conf['fallback'] = 0
    else:
      self.conf['fallback'] = 1

    self._lock_drives()

    # Define the mainloop
    self.mainloop = gobject.MainLoop()
    self.cv = threading.Condition()
    self.gatherer = Gatherer(self.cv, self.discs)
    gobject.timeout_add(1000, self.timeout)

    debug("Ready to rumble!\n")

  def timeout(self):
    self.cv.acquire()
    self.discs.add('OQv2CkDmFRI6XLoYuO7C9EzC1nw-')
    self.cv.notify()
    self.cv.release()
    return False

  def _device_added_callback(self, udi):
    """
      New device added, which means a CD inserted into one of monitored drives
    """

    self.cv.acquire()
    obj = self.bus.get_object('org.freedesktop.Hal', udi)
    vol = dbus.Interface(obj, 'org.freedesktop.Hal.Device')

    """
      If it is an audio disc get it's block device name and use it to retrieve
      data. Try MusicBrainz first and if it fails and FreeDB fallback is set
      try checking the discID in FreeDB forst and then search a release by name
      with MusicBrainz
    """

    if vol.GetProperty('volume.disc.has_audio'):
      debug("New audio CD inserted\n")
      dev = vol.GetProperty('block.device')
      disc_id = None
      if self.conf['fallback']:
        import DiscID, CDDB
        cdrom = DiscID.open(dev)
        disc_id = DiscID.disc_id(cdrom)
      mbdisc_id = mbdisc.readDisc(dev).getId()

      # Eject the device if supported and user wants to
      if self.config.get('eject') != '0':            
        debug("Ejecting CD\n")
        if dbus.Interface(self.bus.get_object('org.freedesktop.Hal', vol.GetProperty('info.parent')), 'org.freedesktop.Hal.Device').GetProperty('storage.requires_eject'):
          obj.Eject([""], dbus_interface="org.freedesktop.Hal.Device.Volume")

      # Add both discIDs to the list
      debug("Adding %s\n" % mbdisc_id)
      self.discs.add(mbdisc_id, disc_id)
      self.cv.notify()
      self.cv.release()

  def getinfo(self, fallback = None):
    """
      Retrieve data for queued discs and print them the pretty way
    """

    if self.discs.len() != 0:
      debug("Getting info\n")
      self.discs.getData(fallback)
      self.discs.prettyPrint()

  def _lock_drives(self):
    """
      Find all CD-ROM devices and lock each of them
    """

    devices = self.hal_manager.FindDeviceByCapability('storage.cdrom')
    for d in devices:
      ld = self.bus.get_object('org.freedesktop.Hal', d)
      try:
        ld.Lock('collector')
      except:
        pass
      else:
        self.locked_devs.append(ld)

  def _unlock_drives(self):
    """
      Unlock devices locked with lock_devices()
    """

    for ld in self.locked_devs:
      ld.Unlock()

  def run(self):
    """
      Mainloop. Currently inifinite loop, interruptible with ^C
    """

    self.gatherer.start()

    try:
      self.mainloop.run()
    except (KeyboardInterrupt, SystemExit):
      self.gatherer.stop()
      self._unlock_drives()

      # Flush the queue
      self.getinfo(self.conf['fallback'])
      self.config.commit()
      return 0

def main(localedir = None):
  collector = Collector(localedir)
  sys.exit(collector.run())
